# gui/main.py
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import subprocess
import os
import threading
import re
import uuid
import json

class PackageConverterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("包格式转换工具")
        self.setup_ui()
        self.dependency_map = self.load_dependency_map()

    def setup_ui(self):
        """初始化用户界面"""
        self.frame = ttk.Frame(self.root, padding="10")
        self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E))

        # 包类型选择
        ttk.Label(self.frame, text="选择包类型:").grid(row=0, column=0, sticky=tk.W)
        self.pkg_type = tk.StringVar(value="deb")
        ttk.Radiobutton(self.frame, text=".deb", variable=self.pkg_type, value="deb").grid(row=0, column=1, sticky=tk.W)
        ttk.Radiobutton(self.frame, text="AppImage", variable=self.pkg_type, value="appimage").grid(row=0, column=2, sticky=tk.W)
        ttk.Radiobutton(self.frame, text="Snap", variable=self.pkg_type, value="snap").grid(row=0, column=3, sticky=tk.W)
        ttk.Radiobutton(self.frame, text="Flatpak", variable=self.pkg_type, value="flatpak").grid(row=0, column=4, sticky=tk.W)

        # 文件选择
        ttk.Label(self.frame, text="选择文件:").grid(row=1, column=0, sticky=tk.W)
        self.file_path = tk.StringVar()
        ttk.Entry(self.frame, textvariable=self.file_path, width=50).grid(row=1, column=1)
        ttk.Button(self.frame, text="浏览", command=self.select_file).grid(row=1, column=2)

        # 转换按钮
        ttk.Button(self.frame, text="开始转换", command=self.start_conversion).grid(row=2, column=1, pady=10)

        # 日志输出
        self.log = tk.Text(self.frame, height=15, width=80)
        self.log.grid(row=3, column=0, columnspan=5, pady=10)

    def load_dependency_map(self):
        """加载依赖映射文件"""
        try:
            with open("dependency_map.json", "r") as f:
                return json.load(f)
        except FileNotFoundError:
            return {"deb": {}, "appimage": {}, "snap": {}, "flatpak": {}}

    def select_file(self):
        """打开文件选择对话框"""
        file_types = [
            ("所有支持格式", "*.deb *.AppImage *.snap *.flatpak"),
            ("Deb 包", "*.deb"),
            ("AppImage", "*.AppImage"),
            ("Snap 包", "*.snap"),
            ("Flatpak 包", "*.flatpak")
        ]
        file_path = filedialog.askopenfilename(filetypes=file_types)
        if file_path:
            self.file_path.set(file_path)
            # 自动检测文件类型
            if file_path.lower().endswith(".appimage"):
                self.pkg_type.set("appimage")
            elif file_path.lower().endswith(".snap"):
                self.pkg_type.set("snap")
            elif file_path.lower().endswith(".flatpak"):
                self.pkg_type.set("flatpak")
            else:
                self.pkg_type.set("deb")

    def start_conversion(self):
        """启动转换任务"""
        file_path = self.file_path.get()
        if not file_path:
            messagebox.showerror("错误", "请先选择文件！")
            return

        # 禁用按钮避免重复点击
        self.frame.children['!button3'].config(state=tk.DISABLED)
        self.log.delete(1.0, tk.END)
        self.log.insert(tk.END, "开始转换...\n")

        # 异步执行转换任务
        threading.Thread(
            target=self.run_conversion,
            args=(file_path, self.pkg_type.get())
        ).start()

    def run_conversion(self, file_path, pkg_type):
        """核心转换逻辑"""
        try:
            temp_dir = f"/tmp/{uuid.uuid4().hex}"
            wsl_file_path = f"{temp_dir}/{os.path.basename(file_path)}"
            output_dir = os.path.join(os.path.dirname(file_path), "linglong-output")
            os.makedirs(output_dir, exist_ok=True)

            # Step 1: 复制文件到 WSL
            self.log_insert("复制文件到 WSL...\n")
            subprocess.run(f"wsl mkdir -p {temp_dir}", shell=True, check=True)
            subprocess.run(f'wsl cp "{file_path}" "{wsl_file_path}"', shell=True, check=True)

            # Step 2: 执行转换逻辑
            if pkg_type == "deb":
                self.convert_deb(wsl_file_path, temp_dir)
            elif pkg_type == "appimage":
                self.convert_appimage(wsl_file_path, temp_dir)
            elif pkg_type == "snap":
                self.convert_snap(wsl_file_path, temp_dir)
            elif pkg_type == "flatpak":
                self.convert_flatpak(wsl_file_path, temp_dir)

            # Step 3: 复制结果回 Windows
            self.log_insert("复制玲珑包到 Windows...\n")
            subprocess.run(f'wsl cp "{temp_dir}/linglong/*.uab" "{output_dir}"', shell=True, check=True)
            messagebox.showinfo("成功", f"转换完成！文件已保存到：\n{output_dir}")

        except subprocess.CalledProcessError as e:
            self.log_insert(f"转换失败: {str(e)}\n")
            messagebox.showerror("错误", "转换失败，请检查日志！")
        except Exception as e:
            self.log_insert(f"未知错误: {str(e)}\n")
            messagebox.showerror("错误", "发生未知错误！")
        finally:
            subprocess.run(f"wsl rm -rf {temp_dir}", shell=True)
            self.frame.children['!button3'].config(state=tk.NORMAL)

    def convert_deb(self, wsl_file_path, temp_dir):
        """处理 .deb 包转换"""
        self.log_insert("开始转换 .deb 包...\n")
        cmd = f'wsl ll-convert-deb "{wsl_file_path}" --output {temp_dir}/linglong'
        self.run_wsl_command(cmd)

    def convert_appimage(self, wsl_file_path, temp_dir):
        """处理 AppImage 转换"""
        self.log_insert("开始转换 AppImage 包...\n")

        # Step 1: 解压 AppImage
        extract_dir = f"{temp_dir}/appimage-extract"
        cmd_extract = f'wsl mkdir -p {extract_dir} && wsl chmod +x "{wsl_file_path}" && wsl "{wsl_file_path}" --appimage-extract --destination {extract_dir}'
        self.run_wsl_command(cmd_extract)

        # Step 2: 分析依赖
        cmd_find_bin = f'wsl find {extract_dir} -type f -executable -print -quit'
        bin_path = subprocess.check_output(cmd_find_bin, shell=True, text=True).strip()
        
        cmd_ldd = f'wsl ldd "{bin_path}"'
        ldd_output = subprocess.check_output(cmd_ldd, shell=True, text=True)
        libs = re.findall(r'=>\s+(/.+?)\s+', ldd_output)

        # Step 3: 生成 linglong.yaml
        yaml_content = f"""package:
  id: com.appimage.converted.{os.path.basename(bin_path)}
  version: 1.0.0
  runtime: org.deepin.Runtime/20/x86_64
  base: minimal

files:
  - from: {extract_dir}/
    to: /

dependencies:
  - runtime: org.deepin.Runtime/20/x86_64
"""

        # 添加依赖映射
        for lib in libs:
            lib_name = os.path.basename(lib).split('.')[0]
            if lib_name in self.dependency_map.get("appimage", {}):
                mapped = self.dependency_map["appimage"][lib_name]
                yaml_content += f"  - {lib_name}: {mapped}\n"

        # 保存配置文件
        cmd_gen_yaml = f'wsl bash -c "echo \'{yaml_content}\' > {temp_dir}/linglong/linglong.yaml"'
        self.run_wsl_command(cmd_gen_yaml)

        # Step 4: 构建玲珑包
        cmd_build = f'wsl mkdir -p {temp_dir}/linglong && cd {temp_dir}/linglong && ll-builder build'
        self.run_wsl_command(cmd_build)

    def convert_snap(self, wsl_file_path, temp_dir):
        """处理 Snap 包转换"""
        self.log_insert("开始转换 Snap 包...\n")
        
        # Step 1: 解压 Snap 包
        extract_dir = f"{temp_dir}/snap-extract"
        cmd_unsquash = f'wsl unsquashfs -d {extract_dir} "{wsl_file_path}"'
        self.run_wsl_command(cmd_unsquash)

        # Step 2: 解析 snap.yaml
        cmd_parse_yaml = f'wsl cat {extract_dir}/meta/snap.yaml'
        yaml_content = subprocess.check_output(cmd_parse_yaml, shell=True, text=True)
        snap_meta = yaml.safe_load(yaml_content)

        # Step 3: 生成 linglong.yaml
        yaml_template = f"""package:
  id: com.snap.converted.{snap_meta['name']}
  version: {snap_meta['version']}
  runtime: org.deepin.Runtime/20/x86_64
  base: minimal

files:
  - from: {extract_dir}/
    to: /

dependencies:
  - runtime: org.deepin.Runtime/20/x86_64
"""
        # 添加依赖映射
        for plug in snap_meta.get('plugs', []):
            if plug in self.dependency_map.get("snap", {}):
                mapped = self.dependency_map["snap"][plug]
                yaml_template += f"  - {plug}: {mapped}\n"

        # 保存配置文件
        cmd_gen_yaml = f'wsl bash -c "echo \'{yaml_template}\' > {temp_dir}/linglong/linglong.yaml"'
        self.run_wsl_command(cmd_gen_yaml)

        # Step 4: 构建玲珑包
        cmd_build = f'wsl mkdir -p {temp_dir}/linglong && cd {temp_dir}/linglong && ll-builder build'
        self.run_wsl_command(cmd_build)

    def convert_flatpak(self, wsl_file_path, temp_dir):
        """处理 Flatpak 转换"""
        self.log_insert("开始转换 Flatpak 包...\n")
        
        # Step 1: 解压并解析元数据
        extract_dir = f"{temp_dir}/flatpak-extract"
        cmd_extract = f'wsl ostree checkout --repo="{wsl_file_path}" :/ {extract_dir}'
        self.run_wsl_command(cmd_extract)

        # Step 2: 解析 metadata.json
        cmd_parse_json = f'wsl cat {extract_dir}/metadata.json'
        json_content = subprocess.check_output(cmd_parse_json, shell=True, text=True)
        metadata = json.loads(json_content)

        # Step 3: 生成 linglong.yaml
        yaml_template = f"""package:
  id: com.flatpak.converted.{metadata['name']}
  version: {metadata['runtime-version']}
  runtime: org.deepin.Runtime/20/x86_64
  base: minimal

files:
  - from: {extract_dir}/
    to: /
"""
        # 添加依赖
        for req in metadata.get('requires', []):
            if req in self.dependency_map.get("flatpak", {}):
                mapped = self.dependency_map["flatpak"][req]
                yaml_template += f"dependencies:\n  - {req}: {mapped}\n"

        # 保存配置文件
        cmd_gen_yaml = f'wsl bash -c "echo \'{yaml_template}\' > {temp_dir}/linglong/linglong.yaml"'
        self.run_wsl_command(cmd_gen_yaml)

        # Step 4: 构建玲珑包
        cmd_build = f'wsl mkdir -p {temp_dir}/linglong && cd {temp_dir}/linglong && ll-builder build'
        self.run_wsl_command(cmd_build)

    def run_wsl_command(self, cmd):
        """执行 WSL 命令并实时输出日志"""
        process = subprocess.Popen(
            cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
        )
        for line in process.stdout:
            self.log_insert(line)
        process.wait()
        if process.returncode != 0:
            raise subprocess.CalledProcessError(process.returncode, cmd)

    def log_insert(self, text):
        """向日志窗口追加内容"""
        self.log.insert(tk.END, text)
        self.log.see(tk.END)

if __name__ == "__main__":
    root = tk.Tk()
    app = PackageConverterApp(root)
    root.mainloop()